#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

#include "simnand.h"


void setnonblocking(int sock)
{
    int opts;

    opts = fcntl(sock, F_GETFL);
    if (opts<0) {
        perror("fcntl(sock,GETFL)");
        exit(1);
    }

    opts = opts | O_NONBLOCK;
    if ( fcntl(sock, F_SETFL, opts) < 0 ) {
        perror("fcntl(sock,SETFL,opts)");
        exit(1);
    }
}


int create_simnand_inst(struct simnand_inst *pparam)
{
    int ret, i, maxi, connfd, sockfd, nfds;
    struct epoll_event ev, events[20];
    struct sockaddr_in clientaddr;
    struct sockaddr_in serveraddr;
    socklen_t clilen;
    ssize_t n;

    fprintf(stdout, "channel %d create simnand inst server.\n", pparam->chid);

    if (NULL == pparam) {
        fprintf(stdout, "channel %d params not right\n", pparam->chid);
        return -1;
    }

    pparam->epfd = epoll_create(MAX_EPOLL_COUNT);
    if (pparam->epfd < 0) {
        fprintf(stderr, "channel %d epoll create fail.\a\n", pparam->chid);
        goto FAIL_QUIT;
    }

    pparam->socketfd = socket(AF_INET, SOCK_STREAM, 0);
    if (pparam->socketfd < 0) {
        fprintf(stderr, "channel %d server socket create fail.\a\n", pparam->chid);
        goto FAIL_QUIT;
    }

    fprintf(stdout, "channel %d create server socket %d\n", pparam->chid, pparam->socketfd);

    //把socket设置为非阻塞方式
    //setnonblocking(listenfd);

    //设置与要处理的事件相关的文件描述符
    ev.data.fd = pparam->socketfd;
    ev.events = EPOLLIN|EPOLLET;
    ret = epoll_ctl(pparam->epfd, EPOLL_CTL_ADD, pparam->socketfd, &ev);
    if (ret < 0) {
        fprintf(stderr, "channel %d epoll ctl fail.\a\n", pparam->chid);
        goto FAIL_QUIT;
    }

    fprintf(stdout, "channel %d epoll_ctl %d\n", pparam->chid, ret);

    bzero(&serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serveraddr.sin_port = htons(pparam->portnum);
    ret = bind(pparam->socketfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
    if (ret < 0) {
        fprintf(stderr, "channel %d bind port %d fail. %d\a\n", pparam->chid, ret, pparam->portnum);
        goto FAIL_QUIT;
    }

    fprintf(stdout, "channel %d bind %d\n", pparam->chid, ret);

    ret = listen(pparam->socketfd, LISTENQ);
    if (ret < 0) {
        fprintf(stderr, "channel %d listen socket fail.\a\n", pparam->chid);
        goto FAIL_QUIT;
    }

    fprintf(stdout, "channel %d listen %d\n", pparam->chid, ret);

    for ( ; ; ) {
        nfds = epoll_wait(pparam->epfd, events, MAX_EPOLL_EVENT, MAX_EPOLL_TIMEOUT);
        if (nfds > 0) {
            fprintf(stdout, "channel %d new event %d\n", pparam->chid, nfds);
        }

        for (i=0; i<nfds; ++i) {    
            if (events[i].data.fd == pparam->socketfd) {
                connfd = accept(pparam->socketfd, (struct sockaddr *)&clientaddr, &clilen);
                if (connfd < 0) {
                    fprintf(stdout, "channel %d connfd < 0\n", pparam->chid);
                }
                //setnonblocking(connfd);

                char *str = inet_ntoa(clientaddr.sin_addr);
                fprintf(stdout, "channel %d accapt a connection from %s\n", pparam->chid, str);

                //设置用于读操作的文件描述符
                ev.data.fd = connfd;
                ev.events = EPOLLIN|EPOLLET;
                ret = epoll_ctl(pparam->epfd, EPOLL_CTL_ADD, connfd, &ev);                
                if (ret < 0) {
                    fprintf(stderr, "channel %d epoll ctl fail.\a\n", pparam->chid);
                }

            } else if (events[i].events & EPOLLIN) {
                fprintf(stdout, "channel %d EPOLLIN\n", pparam->chid);
                if ( (sockfd = events[i].data.fd) < 0 )
                    continue;

                n = simnand_ops(sockfd, pparam, (char*)pparam->ploadbuf, MAXLINE);
                if ( n < 0 ) {
                    fprintf(stdout, "channel %d readline error\n", pparam->chid);
                }

                fprintf(stdout, "channel %d read \n", pparam->chid);

            } else if (events[i].events & EPOLLOUT) {

            }
        }
    }

    return 0;

FAIL_QUIT:
    if (pparam->epfd > 0) {
        close(pparam->epfd);
    }

    if (pparam->socketfd > 0) {
        close(pparam->socketfd);
    }

    return -1;
}


int simnand_ops(int sockfd, struct simnand_inst *psimnand, char *pbuf, int count)
{
    struct simnand_inst *pinst = (struct simnand_inst *)psimnand;
    struct payload *pload = pinst->ploadbuf;
    int socketfd = pinst->socketfd;
    int len = 0, size = 0;
    int result;

    //read socket, get cmd
    len = read(sockfd, pload, sizeof(struct payload) + SECTOR_SIZE);
    printf("recv length: %d\n", len);

    int offset = DIE_OFFSET(pload->bank, pload->block, pload->page);
    fseek(pinst->fp, offset, SEEK_SET);

    switch (pload->cmd_type) {
    case CMD_READ:
        fprintf(stdout, "channel %d read data: %s\n", pinst->chid, pbuf);

        //read file, get data
        size = fread(pload->pbuf, 1, SECTOR_SIZE, pinst->fp);
        if (size <= 0) {
            pload->size = 0;

        } else {
            pload->size = size;
        }

        result = (int)send(sockfd, pload, sizeof(struct payload) + SECTOR_SIZE, 0);
        if (result == -1) {
            fprintf(stderr, "channel %d send error: %s \n", pinst->chid, strerror(errno));
        }

        break;

    case CMD_WRITE:
        fprintf(stdout, "channel %d write data: %s\n", pinst->chid, pbuf);

        //read file, get data
        size = fwrite(pload->pbuf, 1, SECTOR_SIZE, pinst->fp);
        if (size <= 0) {
            pload->size = 0;

        } else {
            pload->size = size;
        }

        result = (int)send(sockfd, pload, sizeof(struct payload), 0);
        if (result == -1) {
            fprintf(stderr, "channel %d send error: %s \n", pinst->chid, strerror(errno));
        }
        break;

    case CMD_ERASE:
        fprintf(stdout, "channel %d write data: %s\n", pinst->chid, pbuf);
        memset(pload->pbuf, 0xFF, SECTOR_SIZE);

        //read file, get data
        size = fwrite(pload->pbuf, 1, SECTOR_SIZE, pinst->fp);
        if (size <= 0) {
            pload->size = 0;

        } else {
            pload->size = size;
        }

        result = (int)send(sockfd, pload, sizeof(struct payload), 0);
        if (result == -1) {
            fprintf(stderr, "channel %d send error: %s \n", pinst->chid, strerror(errno));
        }
        break;

    default:
        printf("recv data: %s\n", (char*)pload);
        break;
    }

    return 0;
}


void printids(const char *s)
{
    pid_t pid;
    pthread_t tid;

    pid = getpid();
    tid = pthread_self();

    fprintf(stdout, "%s pid %u tid %u (0x%x)\n", s, (unsigned int) pid, (unsigned int) tid, (unsigned int) tid);
}


void* simnand_inst_server(void* param)
{
    struct simnand_inst *pparam = (struct simnand_inst *)param;

    printids("channel new thread: ");
    
    pparam->fp = fopen(pparam->pchpath, "wb+");
    if (pparam->fp == NULL) {
        fprintf(stderr, "channel %d open channel file(%s) fail.\n", pparam->chid, pparam->pchpath);
        return NULL;
    }

    pparam->ploadbuf = (struct payload*)malloc(sizeof(struct payload) + SECTOR_SIZE);
    if (pparam->ploadbuf != NULL) {
        pparam->ploadbuf->pbuf = (char*)pparam->ploadbuf + sizeof(struct payload);
    } else {
        fprintf(stderr, "channel %d malloc ploadbuf fail.\n", pparam->chid);
        return NULL;
    }

    create_simnand_inst(pparam);

    fprintf(stdout, "channel %d exit register socket server.\n", pparam->chid);

    return NULL;

FAIL:
    if (pparam->fp !=NULL) {
        fclose(pparam->fp);
    }

    if (pparam->ploadbuf != NULL) {
        free(pparam->ploadbuf);
    }
}

